package com.gateway.bootstrap.handler;

import com.gateway.common.dto.route.FilterData;
import com.gateway.common.dto.route.FilterDefinitionDTO;
import com.gateway.common.dto.route.PredicateData;
import com.gateway.common.dto.route.PredicateDefinitionDTO;
import com.gateway.common.dto.route.RouteData;
import com.gateway.common.enums.RpcTypeEnum;
import com.gateway.common.utils.JsonUtils;
import com.gateway.router.base.handler.RouteDataHandler;
import com.gateway.plugin.base.handler.event.HandlerRefreshEvent;
import com.gateway.repository.bean.GatewayRouteDefinition;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @program: api-gateway
 * @description:
 * @author: YuKai Fan
 * @create: 2025/2/6 15:39
 **/
public class GatewayRouteDataHandler implements RouteDataHandler {
    private final ApplicationEventPublisher publisher;
    private final RouteDefinitionRepository routeDefinitionRepository;

    public GatewayRouteDataHandler(ApplicationEventPublisher publisher, RouteDefinitionRepository routeDefinitionRepository) {
        this.publisher = publisher;
        this.routeDefinitionRepository = routeDefinitionRepository;
    }

    @Override
    public void handle(RouteData routeData) {
        routeDefinitionRepository.save(Mono.just(convert(routeData))).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    @Override
    public void remove(RouteData routeData) {
        routeDefinitionRepository.delete(Mono.just(routeData.getId())).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    @Override
    public void refresh() {
        this.publisher.publishEvent(new HandlerRefreshEvent(this));
    }

    @Override
    public String rpcType() {
        return RpcTypeEnum.SPRING_CLOUD.getName();
    }

    private RouteDefinition convert(RouteData routeData) {
        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId(routeData.getId());
        routeDefinition.setUri(routeData.getUri());
        routeDefinition.setOrder(routeData.getOrder());
        routeDefinition.setMetadata(routeData.getMetadata());
        List<FilterData> filterDataList = routeData.getFilters();
        if (CollectionUtils.isNotEmpty(filterDataList)) {
            List<FilterDefinition> filterDefinitionList = filterDataList.stream()
                    .map(filterData -> {
                        FilterDefinition filterDefinition = new FilterDefinition();
                        filterDefinition.setName(filterData.getName());
                        filterDefinition.setArgs(filterData.getArgs());
                        return filterDefinition;
                    }).collect(Collectors.toList());
            routeDefinition.setFilters(filterDefinitionList);
        }
        List<PredicateData> predicateDataList = routeData.getPredicates();
        if (CollectionUtils.isNotEmpty(predicateDataList)) {
            List<PredicateDefinition> predicateDefinitionList = predicateDataList.stream()
                    .map(predicateData -> {
                        PredicateDefinition predicateDefinition = new PredicateDefinition();
                        predicateDefinition.setName(predicateData.getName());
                        predicateDefinition.setArgs(predicateData.getArgs());
                        return predicateDefinition;
                    }).collect(Collectors.toList());
            routeDefinition.setPredicates(predicateDefinitionList);
        }
        return routeDefinition;
    }

    private RouteDefinition convert(GatewayRouteDefinition routeData) throws URISyntaxException {
        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId(routeData.getRouteId());
        routeDefinition.setUri(new URI(routeData.getUri()));
        routeDefinition.setOrder(routeData.getSortNo());
        String filterDefinitionConfig = routeData.getFilterDefinitionConfig();
        if (StringUtils.isNotBlank(filterDefinitionConfig)) {
            List<FilterDefinitionDTO> filterDataList = JsonUtils.jsonToList(filterDefinitionConfig, FilterDefinitionDTO.class);
            assert filterDataList != null;
            List<FilterDefinition> filterDefinitionList = filterDataList.stream()
                    .map(filterData -> {
                        FilterDefinition filterDefinition = new FilterDefinition();
                        filterDefinition.setName(filterData.getName());
                        filterDefinition.setArgs(filterData.getArgs());
                        return filterDefinition;
                    }).collect(Collectors.toList());
            routeDefinition.setFilters(filterDefinitionList);
        }
        String predicateDefinitionConfig = routeData.getPredicateDefinitionConfig();
        if (StringUtils.isNotBlank(predicateDefinitionConfig)) {
            List<PredicateDefinitionDTO> predicateDataList = JsonUtils.jsonToList(predicateDefinitionConfig, PredicateDefinitionDTO.class);
            assert predicateDataList != null;
            List<PredicateDefinition> predicateDefinitionList = predicateDataList.stream()
                    .map(predicateData -> {
                        PredicateDefinition predicateDefinition = new PredicateDefinition();
                        predicateDefinition.setName(predicateData.getName());
                        predicateDefinition.setArgs(predicateData.getArgs());
                        return predicateDefinition;
                    }).collect(Collectors.toList());
            routeDefinition.setPredicates(predicateDefinitionList);
        }
        return routeDefinition;
    }
}
